package pers.gxm.learning.winter.core.context.impl;

import pers.gxm.learning.winter.core.annotation.ComponentScan;
import pers.gxm.learning.winter.core.annotation.Configuration;
import pers.gxm.learning.winter.core.annotation.WinterBean;
import pers.gxm.learning.winter.core.annotation.WinterDI;
import pers.gxm.learning.winter.core.context.ApplicationContext;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

public class AnnotationConfigApplicationContext implements ApplicationContext {

    private String rootPath;

    private final Map<Class,Object> beanFactory = new HashMap<>();

    @Override
    public <T> T getBean(Class<T> clazz) {
        Set<Class> classes = beanFactory.keySet();
        List<Object> candidates = new ArrayList<>(classes.size());
        for (Class aClass:classes) {
//            判断map中的Class实例对应的类是否继承或实现形参对应的类
            if(clazz.isAssignableFrom(aClass)){
               candidates.add(beanFactory.get(aClass));
            }
        }
        if(candidates.size() > 1){
            throw new RuntimeException("存在多个bean实例无法指定");
        }else if(candidates.size() == 0){
            throw new RuntimeException("不存在bean实例");
        }
        return (T) candidates.get(0);
    }


    /**
     * 对包名为basePackage的包进行扫描，对带有@WinterBean注解的类对象进行创建并管理
     * @param basePackages
     */
    public AnnotationConfigApplicationContext(String... basePackages){
       handleBasePackagesAndLoad(basePackages);
    }

    /**
     *  用@ComponentScan注解进行初始化
     * @param clazz
     */
    public AnnotationConfigApplicationContext(Class<?> clazz){
        if(!clazz.isAnnotationPresent(Configuration.class)){
            throw new RuntimeException("此类不是配置类！");
        }
        if(!clazz.isAnnotationPresent(ComponentScan.class)){
            throw new RuntimeException("未添加包扫描注解!");
        }
        ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
        String[] basePackages = componentScan.value();
       handleBasePackagesAndLoad(basePackages);
    }

    /**
     *  对单个包名进行处理
     * @param basePackage
     */
    private void handleBasePackageAndLoad(String basePackage){
        if(basePackage == null){
            throw new RuntimeException("包名为空");
        }
//        TODO 先对要扫描的包名进行处理
        String directory = basePackage.replace('.','/');
//        TODO 在classpath路径下进行匹配,得到的是绝对路径
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(directory);
            while(urls.hasMoreElements()){
                URL url = urls.nextElement();
//                /D:/LearnSpring6/spring6/winter/target/classes/pers/gxm/learning/winter
//                String allPath = url.getFile();
//                如果路径有中文要先进行转化
               String allPath =  URLDecoder.decode(url.getFile(),"utf-8");
                if(rootPath == null){
                    rootPath = allPath.substring(0,allPath.length() - directory.length());
                }
//                D:\LearnSpring6\spring6\winter\target\classes\pers\gxm\learning\winter
//                file比allPath少一个/
                File file = new File(allPath);
                loadBean(file);
            }
        } catch (IOException e) {
            throw new RuntimeException("包名未找到");
        }
        loadDI();
    }

    /**
     *  对多个包名进行处理
     * @param basePackages
     */
    private void handleBasePackagesAndLoad(String... basePackages){
        if(basePackages== null || basePackages.length == 0){
            throw new  RuntimeException("批量包名为空!");
        }
        for(String basePackage : basePackages){
            handleBasePackageAndLoad(basePackage);
        }
    }
    private void loadBean(File fileParent){
        /**
         * 判断是否 是一个没有任何东西的文件夹
         */
        if(fileParent.isDirectory()){
            File[] children = fileParent.listFiles();
            if(children == null || children.length == 0){
                return;
            }
            for (File child: children) {
                loadBean(child);
            }
        }else{
//            得到包名+文件名,因为文件类型不确定所以不能称之为全类名
            String packageFileName = fileParent.getAbsolutePath().substring(rootPath.length() - 1);
           if(packageFileName.endsWith(".class")){
               handleClassFile(packageFileName);
           }
        }
    }

    private void handleClassFile(String classFileName){
        String classFileName1 = classFileName.substring(0, classFileName.length() - ".class".length());
        String allClassName = classFileName1.replace(File.separatorChar,'.');
        try {
            Class<?> aClass = Class.forName(allClassName);
//           还需要判断是否是接口,枚举类,注解
            if(aClass.isAnnotation() || aClass.isInterface() || aClass.isEnum()){
                return;
            }
            boolean annotationPresent = aClass.isAnnotationPresent(WinterBean.class);
            if(annotationPresent){
                Object instance = aClass.getConstructor().newInstance();
                beanFactory.put(aClass,instance);
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("此全类名不存在");
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 依赖注入在什么时候进行比较合适？map生成以后
     */
    private void loadDI(){
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry: entries ) {
            Class aClass = entry.getKey();
            Object value = entry.getValue();
//            获取此Class对象的所有字段
            Field[] fields = aClass.getDeclaredFields();
            for(Field field : fields){

                if(field.isAnnotationPresent(WinterDI.class)){
//                    在map中找到符合field的值，注意要考虑向上转型,但是不用再进行循环,用map的getBean即可。
                    Class<?> type = field.getType();
                    field.setAccessible(true);
                    try {
                        field.set(value,this.getBean(type));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }

//                    for(Map.Entry<Class, Object> entry2: entries ){
//                        Class aClass2 = entry2.getKey();
//                        if(type.isAssignableFrom(aClass2)){
//                            field.setAccessible(true);
////                            默认将找到的第一个符合的Object对field进行赋值
//                            try {
//                                field.set(entry.getValue(),entry2.getValue());
//                            } catch (IllegalAccessException e) {
//                                throw new RuntimeException(e);
//                            }
//                        }
//
//                    }

                }
            }


        }

    }
}
